package org.jeese.common.base.service.impl;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.jeese.common.base.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

@Service("redisService")
public class RedisServiceImpl implements RedisService {
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	
	/**
	 * 1 redisTemplate.opsForValue();//操作字符串
	 * 2 redisTemplate.opsForHash();//操作hash
	 * 3 redisTemplate.opsForList();//操作list
	 * 4 redisTemplate.opsForSet();//操作set
	 * 5 redisTemplate.opsForZSet();//操作有序set
	 */
	
	/**
     * 加锁 ,应用中当调用当前方法返回if(!lock)时建议抛出异常
     * @param key key
     * @param expectedTime 期望时间值 = 当前系统毫秒值 + 业务容忍超时时间
     * @return 加锁是否成功
     */
	@Override
	public boolean lock(String key, String expectedTime) {
		/* setIfAbsent方法参考:http://redis.cn/commands/hsetnx.html
        *  个人理解: 当key不存在时添加redis列值为value并返回true,如果存在则直接返回false
        * */
        //当不存在key的一列时候设置value并返回true
		if (setnx(key, expectedTime)) {
			return true;
		}
        //判断是否超时
        String oldTime = get(key);
        long currentTimeMillis = System.currentTimeMillis();
		if (!StringUtils.isEmpty(oldTime) && currentTimeMillis > Long.parseLong(oldTime)) {
			String oldValue = getSet(key, expectedTime); // 利用redis是单线程的特性
			if (!StringUtils.isEmpty(oldValue) && oldValue.equals(oldTime)) {
				return true;
			}
		}
        return false;
	}

	@Override
	public void unlock(String key, String expectedTime) {
		String value = get(key);
		if (!StringUtils.isEmpty(value) && value.equals(expectedTime)) {
			del(key);
		}
	}

	/**
     * 字符串类型:存入key-value对象，如果key存在，那么默认更新value
     * @param key
     * @param value
     * @return
     */
	@Override
	public boolean set(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.set(serializer.serialize(key), serializer.serialize(value));
				return true;
			}
		});
	}
	

	@Override
	public boolean setnx(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.setNX(serializer.serialize(key), serializer.serialize(value));
				return true;
			}
		});
	}
	
    @Override
    public boolean setex(final String key,final Long timeout,final String value) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				redisConnection.setEx(serializer.serialize(key), timeout, serializer.serialize(value));
				return true;
			}
		});
    }
	
	@Override
	public String get(final String key) {
		return redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				byte[] value = connection.get(serializer.serialize(key));
				return serializer.deserialize(value);
			}
		});
	}

	@Override
	public String getSet(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				byte[] bytes = connection.getSet(serializer.serialize(key), serializer.serialize(value));
				return serializer.deserialize(bytes);
			}
		});
	}
	
	/**
     * 字符串类型:通过key删除对应的key和value
     * @param key
     * @return
     */
	@Override
	public long del(final String key) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.del(serializer.serialize(key));
			}
		});
	}
	
	@Override
	public boolean hasKey(String key){  
		return redisTemplate.hasKey(key);  
    }  
	
	/**
     * 字符串类型:通过key判断对象是否存在
     * @param key
     * @return
     */
	@Override
	public boolean exists(final String key) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.exists(serializer.serialize(key));
			}
		});
	}

	/**
     * 字符串类型:设置key对应的超时时间
     * @param key
     * @param expireTime
     * @return
     */
	@Override
	public boolean expire(final String key, final long expire) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
            	RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                return redisConnection.expire(serializer.serialize(key),expire);
            }
        });
	}
	
	@Override
    public long getExpire(String key){  
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);  
    }  

	@Override
	public <T> boolean setList(String key, List<T> list) {
		return set(key, JSON.toJSONString(list));
	}

	@Override
	public <T> List<T> getList(String key, Class<T> clz) {
		String json = get(key);
		if (json != null) {
			return JSON.parseArray(json, clz);
		}
		return null;
	}

	@Override
	public <T> boolean setMap(String key, Map<String, T> map) {
		String value = JSON.toJSONString(map);
		return set(key, value);
	}

	@Override
	public <T> Map<String, T> getMap(String key) {
		String json = get(key);
		if (json != null) {
			return JSON.parseObject(json, new TypeReference<Map<String, T>>() {});
		}
		return null;
	}

	@Override
	public long lpush(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.lPush(serializer.serialize(key), serializer.serialize(value));
			}
		});
	}

	@Override
	public long rpush(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.rPush(serializer.serialize(key), serializer.serialize(value));
			}
		});
	}

	@Override
	public String lpop(final String key) {
		return redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				byte[] bytes = connection.lPop(serializer.serialize(key));
				return serializer.deserialize(bytes);
			}
		});
	}
	
	@Override
	public String rpop(final String key) {
		return redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				byte[] bytes = connection.rPop(serializer.serialize(key));
				return serializer.deserialize(bytes);
			}
		});
	}
	
	@Override
	public long incr(final String key) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.incr(serializer.serialize(key));
			}
		});
	}

	@Override
	public long decr(final String key) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.decr(serializer.serialize(key));
			}
		});
	}

	@Override
	public long incrBy(final String key, final long delta) {
		if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");  
        }  
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.incrBy(serializer.serialize(key), delta);
			}
		});
	}

	@Override
	public long decrBy(final String key, final long delta) {
		if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");  
        } 
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.decrBy(serializer.serialize(key), delta);
			}
		});
	}

	@Override
	public long sadd(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.sAdd(serializer.serialize(key), serializer.serialize(value));
			}
		});
	}
	
	@Override
	public Set<byte[]> smembers(final String key) {
		return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
			@Override
			public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.sMembers(serializer.serialize(key));
			}
		});
	}

	@Override
	public long srem(final String key, final String value) {
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.sRem(serializer.serialize(key), serializer.serialize(value));
			}
		});
	}

	@Override
	public boolean srename(final String oldkey, final String newKey) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.rename(serializer.serialize(oldkey), serializer.serialize(newKey));
				return true;
			}
		});
	}

	@Override
	public Set<byte[]> keys(final String pattern) {
		return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
			@Override
			public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.keys(serializer.serialize(pattern));
			}
		});
	}

	@Override
	public boolean zadd(final String key, final double score, final String value) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.zAdd(serializer.serialize(key), score, serializer.serialize(value));
				return true;
			}
		});
	}

	@Override
	public Set<byte[]> zrange(final String key, final long start, final long end) {
		return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
			@Override
			public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.zRange(serializer.serialize(key), start, end);
			}
		});
	}

	@Override
    public boolean hset(final String key, final String field, final String value) {  
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.hSet(serializer.serialize(key), serializer.serialize(field), serializer.serialize(value));
			}
		});
    }  
      
	@Override
    public String hget(final String key, final String field) {  
		return redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				try {
					return new String(connection.hGet(serializer.serialize(key), serializer.serialize(field)), "UTF-8");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				return "";
			}
		});
    }  
      
	@Override
    public long hdel(final String key, final String field) {  
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.hDel(serializer.serialize(key), serializer.serialize(field));
			}
		});
    }  
      
	@Override
    public Map<byte[], byte[]> hgetall(final String key) {  
    	return redisTemplate.execute(new RedisCallback<Map<byte[], byte[]>>() {
			@Override
			public Map<byte[], byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				return connection.hGetAll(serializer.serialize(key));
			}
		});
    }

}
